package com.gct.transcode;

import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.mts.model.v20140618.QueryAnalysisJobListRequest;
import com.aliyuncs.mts.model.v20140618.QueryAnalysisJobListResponse;
import com.aliyuncs.mts.model.v20140618.QueryAnalysisJobListResponse.AnalysisJob;
import com.aliyuncs.mts.model.v20140618.QueryAnalysisJobListResponse.AnalysisJob.Template;
import com.aliyuncs.mts.model.v20140618.QueryJobListRequest;
import com.aliyuncs.mts.model.v20140618.QueryJobListResponse;
import com.aliyuncs.mts.model.v20140618.QueryJobListResponse.Job;
import com.aliyuncs.mts.model.v20140618.SubmitAnalysisJobRequest;
import com.aliyuncs.mts.model.v20140618.SubmitAnalysisJobResponse;
import com.aliyuncs.mts.model.v20140618.SubmitJobsRequest;
import com.aliyuncs.mts.model.v20140618.SubmitJobsResponse;
import com.aliyuncs.mts.model.v20140618.SubmitSnapshotJobRequest;
import com.aliyuncs.mts.model.v20140618.SubmitSnapshotJobResponse;
import com.aliyuncs.mts.model.v20140618.SubmitSnapshotJobResponse.SnapshotJob;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.utils.Base64Helper;

public class Main {
    private static final String MTS_REGION = "cn-shanghai";
    private static final String OSS_REGION = "oss-cn-shanghai";

    private static final String mtsEndpoint = "mts.cn-shanghai.aliyuncs.com";
    private static final String ossEndPoint = "http://oss-cn-shanghai.aliyuncs.com";

    private static String testVideoFilePath = "videos/test-中文.mp4";
    private static String waterMarkFilePath = "watermarks/32x32-中文.png";

    private static String accessKeyId = "";
    private static String accessKeySecret = "";
    private static String pipelineId = "";
    private static String transcodeTemplateId = "";
    private static String waterMarkTemplateId = "";

    private static String inputBucket = "";
    private static String outputBucket = "";

    private static DefaultAcsClient aliyunClient;

    private static final String MAC_NAME = "HmacSHA1";
    private static final String ENCODING = "UTF-8"; 

    public static void main(String[] args) throws ClientException {
        DefaultProfile.addEndpoint(MTS_REGION, MTS_REGION, "Mts", mtsEndpoint);
        aliyunClient = new DefaultAcsClient(DefaultProfile.getProfile(MTS_REGION, accessKeyId, accessKeySecret));

        OSSFileDO inputFile = uploadVideoFile();
        OSSFileDO watermarkFile = uploadWatermarkFile();

        transcodeJobFlow(inputFile, watermarkFile);

        snapshotJobFlow(inputFile);
    }

    private static String signature(Date date) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException {
        return Base64Helper.encode(hmacSHA1Encrypt(
                "PUT" + "\n"
                + date + "\n" 
                + "x-oss-object-acl: public-read\n"
                + "/"+inputBucket+"/",accessKeySecret));
    }

    private static byte[] hmacSHA1Encrypt(String encryptText, String encryptKey) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        byte[] data=encryptKey.getBytes(ENCODING);
        SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
        Mac mac = Mac.getInstance(MAC_NAME);
        mac.init(secretKey);
          
        byte[] text = encryptText.getBytes(ENCODING);
        return mac.doFinal(text);
    }

    private static OSSFileDO uploadVideoFile() {
        return uploadLocalFile(inputBucket, "gct/video/" + UUID.randomUUID().toString() + "-中文.mp4", testVideoFilePath);
//        return uploadLocalFile(inputBucket, UUID.randomUUID().toString() + "-test.txt", testVideoFilePath);
    }

    private static OSSFileDO uploadWatermarkFile() {
        return uploadLocalFile(inputBucket, "gct/watermarks/" + UUID.randomUUID().toString() + "-中文.png", waterMarkFilePath);
    }

    public static OSSFileDO uploadLocalFile(String bucket, String object, String filePath){
        OSSClient client = new OSSClient(ossEndPoint, accessKeyId, accessKeySecret);
        try {
            String utf8FilePath = URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(filePath).getPath(),ENCODING);
            File file = new File(utf8FilePath);
            InputStream content = new FileInputStream(file);

            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentLength(file.length());
            meta.setObjectAcl(CannedAccessControlList.PublicRead);
            Date date = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
            System.out.println(" OSS " + accessKeyId + ":" + signature(date));
            meta.setHeader("Authorization", " OSS " + accessKeyId + ":" + signature(date));
            meta.setHeader("Date", date);
            client.putObject(bucket, object, content, meta);

            String encodedObjectName = URLEncoder.encode(object, ENCODING);
            return new OSSFileDO(OSS_REGION, bucket, encodedObjectName);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("fail@uploadLocalFile FileInputStream");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("fail@uploadLocalFile URLEncoder");
        } catch (InvalidKeyException e) {
            throw new RuntimeException("fail@uploadLocalFile Signature");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("fail@uploadLocalFile Signature");
        }
    }

    private static void transcodeJobFlow(OSSFileDO inputFile, OSSFileDO watermarkFile) {
        systemTemplateJobFlow(inputFile, watermarkFile);

//        userCustomTemplateJobFlow(inputFile, watermarkFile);
    }

    private static void systemTemplateJobFlow(OSSFileDO inputFile, OSSFileDO watermarkFile) {
        String analysisJobId = submitAnalysisJob(inputFile, pipelineId);

        AnalysisJob analysisJob = waitAnalysisJobComplete(analysisJobId);

        List<String> templateIds = getSupportTemplateIds(analysisJob);

        String transcodeJobId = submitTranscodeJob(inputFile, watermarkFile, templateIds.get(0));

        Job transcodeJob = waitTranscodeJobComplete(transcodeJobId);

        String outputBucket  = transcodeJob.getOutput().getOutputFile().getBucket();
        String outputObject  = transcodeJob.getOutput().getOutputFile().getObject();
        String outputFileOSSUrl = getOSSUrl(outputBucket, outputObject);
        System.out.println("Transcode success, the target file url is " + outputFileOSSUrl);
    }

    private static String getOSSUrl(String bucket, String object) {
        try {
            return "http://" + bucket + "." + OSS_REGION + ".aliyuncs.com/" + URLDecoder.decode(object, ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("getOSSUrl URL decode failed");
        }
    }

    private static String submitAnalysisJob(OSSFileDO inputFile, String pipelineId) {
        SubmitAnalysisJobRequest request = new SubmitAnalysisJobRequest();

        request.setInput(inputFile.toJsonString());
        request.setPipelineId(pipelineId);
        SubmitAnalysisJobResponse response = null;
        try {
            response = aliyunClient.getAcsResponse(request);
        } catch (ServerException e) {
            throw new RuntimeException("SubmitAnalysisJobRequest Server failed");
        } catch (ClientException e) {
            e.printStackTrace();
            throw new RuntimeException("SubmitAnalysisJobRequest Client failed");
        }
        return response.getAnalysisJob().getId();
    }

    private static AnalysisJob waitAnalysisJobComplete(String analysisJobId) {
        QueryAnalysisJobListRequest request = new QueryAnalysisJobListRequest();
        request.setAnalysisJobIds(analysisJobId);

        QueryAnalysisJobListResponse response = null;
        try{
            while (true) {
                response = aliyunClient.getAcsResponse(request);

                AnalysisJob analysisJob = response.getAnalysisJobList().get(0);
                String status = analysisJob.getState();
                if ("Fail".equals(status)) {
                    throw new RuntimeException("analysisJob state Failed");
                }
                if ("Success".equals(status)) {
                    return analysisJob;
                }

                Thread.sleep(5 * 1000);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException("waitAnalysisJobComplete Server failed");
        } catch (ClientException e) {
            throw new RuntimeException("waitAnalysisJobComplete Client failed");
        }
    }

    private static List<String> getSupportTemplateIds(AnalysisJob analysisJob) {
        List<String> templateIds = new ArrayList<String>(analysisJob.getTemplateList().size());
        for (Template template : analysisJob.getTemplateList()) {
            templateIds.add(template.getId());
        }
        return templateIds;
    }

    private static String submitTranscodeJob(OSSFileDO inputFile, OSSFileDO watermarkFile, String templateId) {
        JSONObject waterMarkConfig = new JSONObject();
        waterMarkConfig.put("InputFile", watermarkFile.toJson());
        System.out.println(watermarkFile.toJsonString());
//        waterMarkConfig.put("WaterMarkTemplateId", watermarkFile.toJson().get("Object"));//waterMarkTemplateId);
        JSONArray waterMarkConfigArray = new JSONArray();
        waterMarkConfigArray.add(waterMarkConfig);

        String outputOSSObjectKey;
        try {
            outputOSSObjectKey = URLEncoder.encode("mtsdemoproject/transcode/" + UUID.randomUUID().toString() + "-中文.mp4",ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("submitTranscodeJob URL encode failed");
        }
        JSONObject jobConfig = new JSONObject();
        jobConfig.put("OutputObject", outputOSSObjectKey);
        jobConfig.put("TemplateId", templateId);
        jobConfig.put("WaterMarks", waterMarkConfigArray);
        JSONArray jobConfigArray = new JSONArray();
        jobConfigArray.add(jobConfig);

        SubmitJobsRequest request = new SubmitJobsRequest();
        request.setInput(inputFile.toJsonString());
        request.setOutputBucket(outputBucket);
        request.setOutputs(jobConfigArray.toJSONString());
        request.setPipelineId(pipelineId);
        request.setOutputLocation(OSS_REGION);

        Integer outputJobCount = 1;

        SubmitJobsResponse response = null;
        try {
            response = aliyunClient.getAcsResponse(request);
            if(response.getJobResultList().size() != outputJobCount) {
                throw new RuntimeException("SubmitJobsRequest Size failed");
            }
            System.out.println(response.getJobResultList().get(0).getMessage());
            return response.getJobResultList().get(0).getJob().getJobId();
        } catch (ServerException e) {
            throw new RuntimeException("submitTranscodeJob Server failed");
        } catch (ClientException e) {
            throw new RuntimeException("submitTranscodeJob Client failed");
        }
    }

    private static Job waitTranscodeJobComplete(String transcodeJobId) {
        QueryJobListRequest request = new QueryJobListRequest();
        request.setJobIds(transcodeJobId);
        System.out.println("transcodeJobId = " + transcodeJobId);
        QueryJobListResponse response = null;
        try {
            while (true) {
                response = aliyunClient.getAcsResponse(request);

                Job transcodeJob = response.getJobList().get(0);
                String status = transcodeJob.getState();

                if ("TranscodeFail".equals(status)) {
                    throw new RuntimeException("transcodeJob state Failed");
                }

                if ("TranscodeSuccess".equals(status)) {
                    return transcodeJob;
                }

                Thread.sleep(5 * 1000);
            }
        } catch (ServerException e) {
            throw new RuntimeException("waitTranscodeJobComplete Server failed");
        } catch (ClientException e) {
            e.printStackTrace();
            throw new RuntimeException("waitTranscodeJobComplete Client failed");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void userCustomTemplateJobFlow(OSSFileDO inputFile, OSSFileDO watermarkFile) {
        String transcodeJobId = submitTranscodeJob(inputFile, watermarkFile, transcodeTemplateId);

        Job transcodeJob = waitTranscodeJobComplete(transcodeJobId);

        String outputBucket  = transcodeJob.getOutput().getOutputFile().getBucket();
        String outputObject  = transcodeJob.getOutput().getOutputFile().getObject();
        String outputFileOSSUrl = getOSSUrl(outputBucket, outputObject);
        System.out.println("Transcode success, the target file url is " + outputFileOSSUrl);
    }

    private static void snapshotJobFlow(OSSFileDO inputFile) {
        SnapshotJob job = submitSnapshotJob(inputFile);

        String outputBucket  = job.getSnapshotConfig().getOutputFile().getBucket();
        String outputObject  = job.getSnapshotConfig().getOutputFile().getObject();
        String outputFileOSSUrl = getOSSUrl(outputBucket, outputObject);
        System.out.println("snapshot success, the target file url is " + outputFileOSSUrl);
    }

    private static SnapshotJob submitSnapshotJob(OSSFileDO inputFile){
        OSSFileDO outputSnapshotFile = new OSSFileDO();
        outputSnapshotFile.setBucket(outputBucket);
        outputSnapshotFile.setLocation(OSS_REGION);
        try {
            outputSnapshotFile.setObject(URLEncoder.encode("gct/snapshots/" + UUID.randomUUID() + "-中文.jpg",ENCODING));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("submitTranscodeJob URL encode failed");
        }


        JSONObject jobConfig = new JSONObject();
        jobConfig.put("OutputFile", outputSnapshotFile.toJson());
        jobConfig.put("Time", 1000L);   //snapshot time by ms

        SubmitSnapshotJobRequest request = new SubmitSnapshotJobRequest();
        request.setSnapshotConfig(jobConfig.toJSONString());
        request.setInput(inputFile.toJsonString());

        SubmitSnapshotJobResponse response = null;
        try {
            response = aliyunClient.getAcsResponse(request);

            return response.getSnapshotJob();
        } catch (ServerException e) {
            throw new RuntimeException("submitSnapshotJob Server failed");
        } catch (ClientException e) {
            throw new RuntimeException("submitSnapshotJob Client failed");
        }
    }
}
